package m.server.web;

import com.alibaba.fastjson.JSONAware;
import m.server.web.req.Header;
import m.server.web.req.RequestExecption;
import m.server.web.res.ContentDisposition;
import m.server.web.res.ContentType;
import m.server.web.res.StatusCode;
import m.system.RuntimeData;
import m.system.exception.MException;
import m.system.lang.HtmlBody;
import m.system.lang.IJsonOuter;
import m.system.url.UrlMarker;
import m.system.util.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class Response {
    private final OutputStream output;
    private final Request request;
    private final Map<String,String> header;
    private boolean isOut=false;
    public Response(OutputStream output,Request request){
        this.output = output;
        this.request=request;
        this.header=new HashMap<>();
        if(null==request.getCookie(AuthInfoUtil.SESSION_NAME)){
            this.header.put(Header.SetCookie.getKey(), AuthInfoUtil.SESSION_NAME+"="+GenerateID.generatePrimaryKey()+";path=/;HttpOnly=true");
        }
    }
    /**
     * 设置header
     */
    public void setHeader(Header key,String value){
        this.header.put(key.getKey(),value);
    }

    /**
     * 设置header
     */
    public void setHeader(String key,String value){
        this.header.put(key,value);
    }
    /**
     * 销毁
     */
    public void destroy() {
        request.destroy();
    }
    /**
     * 输出路径文件
     */
    public void outPath(String path) {
        String p=Request.decode(path);
        File file;
        if(FileUtil.existsWebFile(p)) {//寻找web目录下文件
            file=new File(RuntimeData.getClassWeb()+p);
        }else if(FileUtil.existsPublicFile(p)) {//寻找公共web目录下文件
            file=new File(RuntimeData.getPublicWeb()+p);
        }else {//不存在则找临时目录
            String ps=RuntimeData.getClassTmp()+p;
            file=new File(ps);
            if(!file.exists()) {
                try {//临时目录不存在则从jar包内拷贝到临时目录
                    InputStream is=this.getClass().getClassLoader().getResourceAsStream("web"+(p.indexOf("/")==0?"":"/")+p);
                    if(null!=is) FileUtil.writeFile(ps, is);
                } catch (Exception e) {//不存在则404
                    throw new RequestExecption(StatusCode.NotFound,p+" Not Found");
                }
            }
        }
        //输出路径是请求路径的开头，且符合缓存url
        if(this.request.getUri().indexOf(path)==0&&UrlMarker.isCacheUrl(path)){
            setHeader(Header.CacheControl,"private, max-age=21600");//缓存6个小时
        }
        ContentType type=ContentType.get(p.lastIndexOf(".")>=0?p.substring(p.lastIndexOf(".")+1):"");
        outFile(file,type,ContentDisposition.inline());
    }
    /**
     * 输出文件
     */
    public void outFile(File file,ContentType type, ContentDisposition disposition) {
        try {
            RandomAccessFile raf = new RandomAccessFile(file, "r");
            setHeader(Header.ContentLength, String.valueOf(raf.length()));
            setHeader(Header.ContentDisposition, disposition.toString());
            //输出
            out(raf,type);
        }catch (IOException e){
            throw new RequestExecption(StatusCode.NotFound,"NotFound!");
        }
    }
    public void outImage(BufferedImage image, String suffix) throws MException {
        outImage(image,ContentType.get(suffix));
    }
    /**
     * 输出图片
     */
    public void outImage(BufferedImage image, ContentType type) throws MException {
        try {
            ByteArrayOutputStream os=new ByteArrayOutputStream();
            ImageIO.write(image, type.name(), os);
            byte[] bs=os.toByteArray();
            out(bs,type);
        }catch (IOException e){
            throw new RequestExecption(StatusCode.NotFound,"NotFound!");
        }
    }
    public void out(Object msg){
        out(msg,StatusCode.OK,null);
    }
    public void out(Object msg,StatusCode status){
        out(msg,status,null);
    }
    public void out(Object msg,ContentType type){
        out(msg,StatusCode.OK,type);
    }
    /**
     * 输出信息 一次请求只可以调一次
     */
    public void out(Object msg,StatusCode status,ContentType type){
        try {
            if(null==type){
                type=ContentType.OTHER;
            }
            printLine(request.getProtocol(), " ", String.valueOf(status.getCode()), " ", status.getMsg());
            printLine(Header.ContentType.toString(),": ", type.toString(), "; charset=UTF-8");
            printLine(Header.Connection.toString(),": ", request.getHeader(Header.Connection));
            for(String h : header.keySet()){//自定义header
                printLine(h,": ",header.get(h));
            }
            Map<String,String> map= UrlMarker.getResponseHeaderMap();
            for(String k : map.keySet()){//添加公共的header属性 urlmarker.xml配置
                printLine(k,": ",map.get(k));
            }
            printLine();//空行
            printObject(msg);
            printOver();
            isOut=true;
        }catch (Exception e){
            throw new RequestExecption(StatusCode.ServiceUnavailable,"输出错误异常："+e.getMessage());
        }
    }
    private void printOver() throws IOException {
        output.flush();
        output.close();
    }
    private void printLine(String... ss) throws IOException {
        for (String s : ss) {
            output.write(s.getBytes(StandardCharsets.UTF_8));
        }
        output.write("\r\n".getBytes(StandardCharsets.UTF_8));
    }
    private void printObject(Object msg) throws MException, IOException {
        if(msg instanceof RandomAccessFile) {
            RandomAccessFile r = (RandomAccessFile) msg;
            byte[] bs = new byte[1024];
            int n;
            while ((n = r.read(bs)) > 0) {
                if (n == 1024) output.write(bs);
                else output.write(bs, 0, n);
            }
        }else if(msg instanceof byte[]){
            byte[] bs=(byte[]) msg;
            output.write(bs);
        }else {
            output.write(ObjectUtil.toString(msg).getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * 重定向执行Action
     */
    public Object redirect(ActionRedirect ar) {
        try {
            //执行action的方法-----------------------------------
            Object result = ar.toResult(request,this);
            if(null==result) {
                if(isOut) return null;
                else throw new MException("result:<null>");
            }else if (result instanceof ActionRedirect) {//重定向类
                result = redirect((ActionRedirect) result);
            }
            return result;
        }catch (Exception e){
            if(e instanceof RequestExecption) return e;
            SystemUtil.println("Response redirect:",e.getMessage());
            if(RuntimeData.getDebug()&&!RuntimeData.getOutLog()){
                e.printStackTrace();
            }
            String error;
            MException me=null;
            if(e instanceof InvocationTargetException){
                Throwable target=((InvocationTargetException)e).getTargetException();
                if(target instanceof MException) me=(MException) target;
                error=target.getMessage();
            }else{
                if(e instanceof MException) me=(MException) e;
                error=e.getMessage();
            }
            if(null!=me) {
                ActionRedirect redirect=me.getRedirect();
                if(null!=redirect){//异常包含重定向
                    return redirect(redirect);
                }
            }
            Type type = ar.toResultType();
            if(null!=type) {
                if(IJsonOuter.class.isAssignableFrom((Class)type)
                || JSONAware.class.isAssignableFrom((Class)type)){
                    return JSONMessage.error(error);
                }else if(HtmlBody.class.isAssignableFrom((Class)type)){
                    return new HtmlBody(error).setStatus(ServerResultStatus.EXCEPTION);
                }
            }
            return e;
        }
    }
}
